//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include "sharedmem.h"
#include "pcentral.h"
#include "namedobj.h"
#include <aura.h>

#define Lock(hCritical)     do { \
                                AuraCriticalSection_Enter(hCritical); \
                            } while(0)

#define Unlock(hCritical)   do { \
                                AuraCriticalSection_Leave(hCritical); \
                            } while(0)

CSharedMemory::~CSharedMemory()
{
    if (NULL != m_hSharedMemory) {
        AuraSharedMemory_Destroy(m_hSharedMemory);
        m_hSharedMemory = NULL;
    }

    if (NULL != m_hLock) {
        AuraCriticalSection_Destroy(m_hLock);
        m_hLock = NULL;
    }

    if (NULL != m_pName) {
        WStringBuf::Free(m_pName);
        m_pName = NULL;
    }
}

ECode CSharedMemory::Initialize(
        /* [in] */ WString name,
        /* [in] */ MemorySize size,
        /* [in] */ Boolean bCommit)
{
    assert(!name.IsNullOrEmpty());

    Int32 length = name.GetLength(AURA_MAX_PATH) + 1;
    ECode ec = NOERROR;

    m_pName = WStringBuf::Alloc(length);
    if (NULL == m_pName) {
        goto ErrorExit;
    }
    m_pName->Copy(name);

    ec = AuraCriticalSection_Create(&m_hLock);
    if (FAILED(ec)) {
        goto ErrorExit;
    }
    ec = AuraSharedMemory_Create((Aura_PWChar)(const wchar_t *)name, (Aura_MemorySize)size,
                             (Aura_Bool)bCommit, AURA_TRUE,
                             &m_hSharedMemory);
    if (FAILED(ec)) {
        goto ErrorExit;
    }

    return ec;

ErrorExit:
    if (NULL != m_hLock) {
        AuraCriticalSection_Destroy(m_hLock);
        m_hLock = NULL;
    }
    if (NULL != m_pName) {
        WStringBuf::Free(m_pName);
        m_pName = NULL;
    }
    return ec;
}

PInterface CSharedMemory::Probe(
        /* [in]  */ REIID riid)
{
    if (riid == EIID_IInterface) {
        return (PInterface)(ISharedMemory *)this;
    }
    else if (riid == EIID_ISharedMemory) {
        return (ISharedMemory *)this;
    }
    else if (riid == EIID_CLASS_INFO) {
        return (PInterface)&ECLSID_CSharedMemory;
    }
    else if (riid == EIID_INTERFACE_INFO) {
        return (PInterface)&EIID_ISharedMemory;
    }

    return NULL;
}

UInt32 CSharedMemory::AddRef()
{
    Int32 nRefs = m_cRefs.Increment();

    return (UInt32)nRefs;
}

UInt32 CSharedMemory::Release()
{
    Int32 nRefs = m_cRefs.Decrement();
    if (0 == nRefs) {
        CSharedMemoryFactory factory(*m_pName);
        NamedObject::S_DestroyObject(&factory, m_oid);
        delete this;
    }
    return (UInt32)nRefs;
}

ECode CSharedMemory::Aggregate(
        /* [in]  */ AggregateType type,
        /* [in]  */ PInterface pObject)
{
    return E_NOT_IMPLEMENTED;
}

ECode CSharedMemory::GetDomain(
        /* [out] */ PInterface * ppObj)
{
    return E_NOT_IMPLEMENTED;
}

ECode CSharedMemory::Attach(
        /* [in]  */ MemoryProtection protect,
        /* [out] */ Address * pAddress)
{
    ECode ec = NOERROR;

    if (NULL == pAddress) {
        return E_INVALID_ARGUMENT;
    }

    Lock(m_hLock);
    if (m_attachTimes <= 0) {
        ec = AuraSharedMemory_Attach(m_hSharedMemory, (Aura_MemoryProtection)protect, (Aura_Address *)&m_address);
        if (FAILED(ec)) {
            Unlock(m_hLock);
            return ec;
        }
    }
    m_attachTimes++;
    Unlock(m_hLock);

    *pAddress = m_address;

    return ec;
}

ECode CSharedMemory::Detach()
{
    ECode ec = NOERROR;

    Lock(m_hLock);
    switch (m_attachTimes) {
    case 0:
        ec = E_INVALID_OPERATION;
        break;
    case 1:
        ec = AuraSharedMemory_Detach(m_hSharedMemory, (Aura_Address)m_address);
        if (FAILED(ec)) {
            break;
        }
        m_attachTimes--;
        break;
    default:
        assert(m_attachTimes > 1);
        m_attachTimes--;
        break;
    }
    Unlock(m_hLock);

    return ec;
}

ECode CSharedMemoryFactory::CreateNamedObject(
            /* [out] */ NamedObject ** ppNamedObj,
            /* [out] */ Boolean * pbAlreadyExisted)
{
    CSharedMemory * pSharedMemory = NULL;
    ECode ec = NOERROR;
    oid_t oid = 0;

    assert(NULL != ppNamedObj);
    assert(NULL != pbAlreadyExisted);

    ec = CPseudoCentral::CreateSharedMemory(m_name,
                                        m_size, m_bCommit,
                                        pbAlreadyExisted,
                                        &oid);
    if (FAILED(ec)) {
        goto ErrorExit;
    }

    pSharedMemory = new CSharedMemory(oid);
    if (NULL == pSharedMemory) {
        ec = E_OUT_OF_MEMORY;
        goto ErrorExit;
    }
    ec = pSharedMemory->Initialize(m_name, m_size, m_bCommit);
    if (FAILED(ec)) {
        goto ErrorExit;
    }
    pSharedMemory->AddRef();
    *ppNamedObj = pSharedMemory;

    return ec;

ErrorExit:
    if (NULL != pSharedMemory) {
        delete pSharedMemory;
        pSharedMemory = NULL;
    }
    if (oid > 0) {
        CPseudoCentral::ReleaseNamedObject(oid);
    }
    return ec;
}

ECode CSharedMemoryFactory::ResolveNamedObject(
            /* [in]  */ oid_t oid,
            /* [out] */ NamedObject ** ppNamedObj)
{
    CSharedMemory * pSharedMemory = NULL;
    ECode ec = NOERROR;

    assert(NULL != ppNamedObj);

    pSharedMemory = new CSharedMemory(oid);
    if (NULL == pSharedMemory) {
        ec = E_OUT_OF_MEMORY;
        goto ErrorExit;
    }
    ec = pSharedMemory->Initialize(m_name, m_size, m_bCommit);
    if (FAILED(ec)) {
        goto ErrorExit;
    }

    ec = CPseudoCentral::AddRefNamedObject(oid);
    if (FAILED(ec)) {
        goto ErrorExit;
    }
    pSharedMemory->AddRef();
    *ppNamedObj = pSharedMemory;

    return ec;

ErrorExit:
    if (NULL != pSharedMemory) {
        delete pSharedMemory;
        pSharedMemory = NULL;
    }
    return ec;
}

ECode CSharedMemoryFactory::DestroyNamedObject(
            /* [in]  */ oid_t oid)
{
    return CPseudoCentral::ReleaseNamedObject(oid);
}

